//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#ifndef _UX_RESOURCE_MANAGER_H
#define _UX_RESOURCE_MANAGER_H

#include <Includes.h>
#include "../../Interfaces.h"

namespace Engine
{
namespace CoreSystems
{


	//
	// Resource Manager
	//

	class _EXPORT_ CResourceManager : public CEngBaseObject, public IResourceManager
	{
		friend class Engine::CoreSystems::CCore;

	protected:
		struct TResLoader
		{
			IResourceLoader *	pLoader;
			static_string<16>	sFileExt;
			///
			TResLoader(): pLoader(nullptr), sFileExt() {}
			TResLoader(IResourceLoader *loader, const uni_c_string &ext): pLoader(loader), sFileExt(ext) {}
		};

		struct TResInfo
		{
			array< TResLoader >		aLoaders;
			PCreateResourceProc_t	pCreateProc;
			void	*				pParameter;
			IResourcePtr			pDefaultRes;
			string					sDefPath;
			e_resource::type		eResType;
			///
			TResInfo():
				pDefaultRes(nullptr), pCreateProc(nullptr), pParameter(nullptr), eResType(e_resource::UNKNOWN) {}

			TResInfo(e_resource::type type, PCreateResourceProc_t proc, void *param, const uni_c_string &path, IResource *def = nullptr):
				pDefaultRes(def), pCreateProc(proc), pParameter(param), eResType(type), sDefPath(path) {}
		};

		typedef static_array< IResourceGroup *,
							e_res_group::_MAX_GROUPS,
							TFastCopyStrategy< IResourceGroup *> >	resgroup_array_t;
		typedef array< TResInfo >									resinfo_array_t;


	protected:
		resinfo_array_t			_aResInfo;
		resgroup_array_t		_aResGroups;

		float					_fLastTime;				// in seconds
		const float				_fFreeResTimeInterval;

		TReadWriteSync			_sRWSLockRes;
		e_resman_flags::type	_eFlags;


	protected:
		CResourceManager();
		~CResourceManager();

		bool _Init(const uni_c_string &sDataPath, e_resman_flags::type eFlags);
		void _InitResources();
		
		TResInfo &			_GetResInfo(e_resource::type eType);
		IResourceGroup *	_GetResGroup(e_res_group::type eType);

	public:
		void Clear();
		void FreeAllResources();
		//void FreeNonRefResources();
		void Update();
		
		bool AddResource(const IResourcePtr &, e_res_group::type, IResourceGroup::resourceID_t, IResourceLoader *);
		bool RemoveResource(const IResourcePtr &);

		bool LoadResource(IResourcePtr &, e_resource::type, const uni_array<const uni_c_string> &, bool bAsync = false);
		bool LoadResource(IResourcePtr &, e_resource::type, const IFilePtr &, bool bAsync = false);

		bool SaveResource(const uni_c_string &, const IResourcePtr &);
		bool SaveResource(const IFilePtr &, const IResourcePtr &);
		

		// IResourceManager //
		bool CreateResource(e_resource::type, IResourcePtr &);
		bool RegisterResource(e_resource::type, PCreateResourceProc_t, void *, const uni_c_string &, const IResourcePtr &);
		bool UnregisterResource(e_resource::type) = 0;
		
		bool SetDefaultResource(e_resource::type, const IResourcePtr &);
		bool GetDefaultResource(e_resource::type, IResourcePtr &);
		bool SetDefaultPath(e_resource::type, const uni_c_string &);
		bool GetDefaultPath(e_resource::type, uni_c_string &);

		bool RegisterGroup(e_res_group::type, IResourceGroup *);
		bool UnregisterGroup(e_res_group::type);
		bool GetGroup(e_res_group::type, IResourceGroup *&);

		bool RegisterLoader(const uni_c_string &, IResourceLoader *);
		bool UnregisterLoader(IResourceLoader *);
		bool GetLoader(e_resource::type, const uni_c_string &, IResourceLoader *&);
	};

	/*

		friend class Engine::CoreSystems::CCore;

	private:
		
		//
		// Resource Info
		//

		struct TResInfo
		{
			typedef array< CResourcePtr >	res_array_t;

			res_array_t			aResources;
			PCreateResProc_t	pProc;
			CResourcePtr		pDefault;
			string				sPath;		// path for directory with resources
			e_resource::type		eResType;
			///
			TResInfo():
				eResType(e_resource::UNKNOWN), pProc(nullfunc), pDefault(nullptr) {}

			TResInfo(e_resource::type etype):
				eResType(etype), pProc(nullfunc), pDefault(nullptr) {}
			
			~TResInfo()
			{
				aResources.Clear();
			}
		};

		//
		// Resource Group
		//

		struct TResGroup
		{
			string				sPath;
			PGetResTypeProc_t	pProc;
			e_res_group::type	eGroupType;
			///
			TResGroup(e_res_group::type type, PGetResTypeProc_t proc, const char *path):
				sPath(path), pProc(proc), eGroupType(type) {}
		};

		//
		// Shared Resource
		//

		struct TSharedRes
		{
			string			sName;
			CResourcePtr	pRes;
			///
			TSharedRes() {}
			TSharedRes(const string &name, const CResourcePtr &res): sName(name), pRes(res) {}
		};


	private:
		array< TResInfo *>			_aResources;
		array< TResGroup >			_aResGroups;
		array< TSharedRes >			_aSharedRes;
		thread_array_t				_aLoadThreads;

		float						_fLastTime;				// in seconds
		const float					_fFreeResTimeInterval;

		TReadWriteSync				_sRWSLockRes;
		e_resman_flags::type		_eFlags;

	private:
		TResInfo * _GetResInfo(e_resource::type eResType);

		void _InitResources();
		bool _PushOp(IOperation *pOp, bool bHighPriority);
		bool _CreateRes(e_resource::type eType, CResourcePtr &pResource, const char *pFileName, flag_t uFlags);
		bool _Load(e_resource::type eType, CResourcePtr &pResource, IFilePtr pFile, const char *pFileName, flag_t uFlags = 0, bool bIsDef = false);
		bool _Load(e_res_group::type eType, CResourcePtr &pResource, IFilePtr pFile, const char *pFileName, flag_t uFlags = 0);
		bool _LoadInThread(e_resource::type eType, CResourcePtr &pResource, IFilePtr pFile, const char *pFileName, flag_t uFlags = 0, bool bHighPriority = false);
		bool _LoadInThread(e_res_group::type eType, CResourcePtr &pResource, IFilePtr pFile, const char *pFileName, flag_t uFlags = 0, bool bHighPriority = false);
		bool _Compare(TFileAddress *pFA, e_resource::type eType, CResourcePtr &pRes);
		bool _CompareWithLoaded(CResourcePtr &pRes);
		
		bool _ResCreate(e_resource::type eType, CResourcePtr &pResource, const char *pFileName = nullptr, bool bAddToList = true);
		bool _ResLoad(e_resource::type  eType, CResourcePtr &pResource, const char *pFileName, flag_t uFlags = 0);
		bool _ResLoad(e_res_group::type eType, CResourcePtr &pResource, const char *pFileName, flag_t uFlags = 0);
		bool _ResLoad(e_resource::type  eType, CResourcePtr &pResource, IFilePtr pFile, flag_t uFlags = 0);
		bool _ResLoad(e_res_group::type eType, CResourcePtr &pResource, IFilePtr pFile, flag_t uFlags = 0);

		bool _ResStreamLoad(e_resource::type  eType, CResourcePtr &pResource, const char *pFileName, flag_t uFlags = 0, bool bHighPriority = false);
		bool _ResStreamLoad(e_res_group::type eType, CResourcePtr &pResource, const char *pFileName, flag_t uFlags = 0, bool bHighPriority = false);
		bool _ResStreamLoad(e_resource::type  eType, CResourcePtr &pResource, IFilePtr pFile, flag_t uFlags = 0, bool bHighPriority = false);
		bool _ResStreamLoad(e_res_group::type eType, CResourcePtr &pResource, IFilePtr pFile, flag_t uFlags = 0, bool bHighPriority = false);
		
		bool _GetResourceForName(const string &sName, e_resource::type eType, CResourcePtr &pRes);
		bool _GetResourceForPath(const char *pFileName, e_resource::type eType, CResourcePtr &pRes);
		bool _GetDefaultResource(e_resource::type eType, CResourcePtr &pRes);

		bool _GetSharedResource(const string &sName, CResourcePtr &pRes);

	protected:
		CResourceManager();
		~CResourceManager();

		bool Init(const TEngExtParams &sParams);

		// CEngBaseObject //
		virtual void Release();

	public:
		void Clear();
		void FreeAllResources();
		void FreeNonRefResources();
		void Update();

		bool AddResource(const CResourcePtr &pRes);
		bool RemoveResource(const CResourcePtr &pRes);
		template <typename T> bool GetResourceForName(const string &sName, e_resource::type eType, CRefCounter<T,CReferenced> &pRes);
		template <typename T> bool GetResourceForPath(const char *pFileName, e_resource::type eType, CRefCounter<T,CReferenced> &pRes);
		template <typename T> bool GetDefaultResource(e_resource::type eType, CRefCounter<T,CReferenced> &pRes);

		// resource //
		bool RegisterResource(e_resource::type eType, PCreateResProc_t pProc, const char *pPath = nullptr, const CResourcePtr &pDefault = nullptr);
		bool UnRegisterResource(e_resource::type eType);
		bool ChangeDefaultResource(e_resource::type eType, const CResourcePtr &pRes);
		bool ChangeResourceDir(e_resource::type eType, const char *pPath);
		bool ReplaceResource(CResourcePtr pRes1, CResourcePtr pRes2) const;

		// resource group //
		bool RegisterResourceGroup(e_res_group::type eType, PGetResTypeProc_t pProc, const char *pPath = nullptr);
		bool UnRegisterResourceGroup(e_res_group::type eType);

		// shared resource //
		bool AddSharedResource(const string &sName, const CResourcePtr &pRes);
		bool RemoveSharedResource(const string &sName, CResourcePtr &pRes);
		bool FreeSharedResource(const string &sName);
		template <typename T> bool GetSharedResource(const string &sName, CRefCounter<T,CReferenced> &pRes);

		// For MetaData custom type //
		static IMetaCustomObject* GetMetaObject(e_resource::type eType);

		e_resman_flags::type	GetFlags()		const	{ return _eFlags; }

		// loader //
		template <typename T> bool Create(e_resource::type, CRefCounter<T,CReferenced> &, const char * = nullptr, bool = true);
		template <typename T> bool Load(e_resource::type,  CRefCounter<T,CReferenced> &, const char *, flag_t = 0);
		template <typename T> bool Load(e_res_group::type, CRefCounter<T,CReferenced> &, const char *, flag_t = 0);
		template <typename T> bool Load(e_resource::type,  CRefCounter<T,CReferenced> &, IFilePtr, flag_t = 0);
		template <typename T> bool Load(e_res_group::type, CRefCounter<T,CReferenced> &, IFilePtr, flag_t = 0);

		template <typename T> bool StreamLoad(e_resource::type,  CRefCounter<T,CReferenced> &, const char *, flag_t = 0, bool = false);
		template <typename T> bool StreamLoad(e_res_group::type, CRefCounter<T,CReferenced> &, const char *, flag_t = 0, bool = false);
		template <typename T> bool StreamLoad(e_resource::type,  CRefCounter<T,CReferenced> &, IFilePtr, flag_t = 0, bool = false);
		template <typename T> bool StreamLoad(e_res_group::type, CRefCounter<T,CReferenced> &, IFilePtr, flag_t = 0, bool = false);

	private:
		static bool CALLBACK _s_CreateTexture2DMSArray(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateTexture2DArray(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateTexture2DMS(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateBitmapFont(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateTexture3D(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateTexture2D(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateSprite2D(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateCubeMap(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateTBO(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		
		static bool CALLBACK _s_CreatePostProcess(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateProgramSource(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateShaderProgram(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateProgramShell(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateMaterial(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateShader(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);

		static bool CALLBACK _s_CreateMesh(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateMultiMesh(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateSceletalMesh(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);

		static bool CALLBACK _s_CreateFrameAnim(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateSceletalAnim(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		
		static bool CALLBACK _s_CreateMeshModel(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateModelRange(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateMultiMeshModel(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateFrameAnimator(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateSceletalAnimator(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);

		static bool CALLBACK _s_CreateSoundBuffer(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);
		static bool CALLBACK _s_CreateStreamedSound(CResourcePtr &pRes, const char *pFileName, flag_t uFlags);

		static bool CALLBACK _s_GetTextureType(IFilePtr pFile, e_resource::type &eType);
		static bool CALLBACK _s_GetMeshType(IFilePtr pFile, e_resource::type &eType);
		static bool CALLBACK _s_GetModelType(IFilePtr pFile, e_resource::type &eType);
		static bool CALLBACK _s_GetModelAnimatorType(IFilePtr pFile, e_resource::type &eType);

		// free non reference resources operation proc //
		static void _s_FreeNonRefRes(void *pParametr);

		CResourcePtr _CreateDefaultFont()			const;
		CResourcePtr _CreateDefaultTexture2D()		const;
		CResourcePtr _CreateDefaultSprite2D()		const;
		CResourcePtr _CreateDefaultTexture3D()		const;
		CResourcePtr _CreateDefaultTexBuffer()		const;
		CResourcePtr _CreateDefaultCubeMap()		const;
		CResourcePtr _CreateDefaultShader()			const;
		CResourcePtr _CreateDefaultShaderProgram()	const;
		CResourcePtr _CreateDefaultProgramSource()	const;
		CResourcePtr _CreateDefaultMaterial()		const;
		CResourcePtr _CreateDefaultProgramShell()	const;
		CResourcePtr _CreateDefaultPostProcess()	const;
		CResourcePtr _CreateDefaultBaseMesh()		const;
		CResourcePtr _CreateDefaultSoundBuffer()	const;
		CResourcePtr _CreateDefaultStreamedSound()	const;
	};



	template <typename T>
	bool CResourceManager::GetResourceForName(const string &sName, e_resource::type eType, CRefCounter<T,CReferenced> &pRes)
	{
		return _GetResourceForName( sName, eType, (CResourcePtr &)pRes );
	}
	
	template <typename T>
	bool CResourceManager::GetResourceForPath(const char *pFileName, e_resource::type eType, CRefCounter<T,CReferenced> &pRes)
	{
		return _GetResourceForPath( pFileName, eType, (CResourcePtr &)pRes );
	}
	
	template <typename T>
	bool CResourceManager::GetDefaultResource(e_resource::type eType, CRefCounter<T,CReferenced> &pRes)
	{
		return _GetDefaultResource( eType, (CResourcePtr &)pRes );
	}

	template <typename T>
	bool CResourceManager::GetSharedResource(const string &sName, CRefCounter<T,CReferenced> &pRes)
	{
		return _GetSharedResource( sName, (CResourcePtr &)pRes );
	}
	
	template <typename T>
	bool CResourceManager::Create(e_resource::type eType, CRefCounter<T,CReferenced> &pResource, const char *pFileName, bool bAddToList)
	{
		return _ResCreate( eType, (CResourcePtr &)pResource, pFileName, bAddToList );
	}
	
	template <typename T>
	bool CResourceManager::Load(e_resource::type  eType, CRefCounter<T,CReferenced> &pResource, const char *pFileName, flag_t uFlags)
	{
		return _ResLoad( eType, (CResourcePtr &)pResource, pFileName, uFlags );
	}
	
	template <typename T>
	bool CResourceManager::Load(e_res_group::type eType, CRefCounter<T,CReferenced> &pResource, const char *pFileName, flag_t uFlags)
	{
		return _ResLoad( eType, (CResourcePtr &)pResource, pFileName, uFlags );
	}
	
	template <typename T>
	bool CResourceManager::Load(e_resource::type  eType, CRefCounter<T,CReferenced> &pResource, IFilePtr pFile, flag_t uFlags)
	{
		return _ResLoad( eType, (CResourcePtr &)pResource, pFile, uFlags );
	}
	
	template <typename T>
	bool CResourceManager::Load(e_res_group::type eType, CRefCounter<T,CReferenced> &pResource, IFilePtr pFile, flag_t uFlags)
	{
		return _ResLoad( eType, (CResourcePtr &)pResource, pFile, uFlags );
	}

	template <typename T>
	bool CResourceManager::StreamLoad(e_resource::type  eType, CRefCounter<T,CReferenced> &pResource, const char *pFileName, flag_t uFlags, bool bHighPriority)
	{
		return _ResStreamLoad( eType, (CResourcePtr &)pResource, pFileName, uFlags, bHighPriority );
	}
	
	template <typename T>
	bool CResourceManager::StreamLoad(e_res_group::type eType, CRefCounter<T,CReferenced> &pResource, const char *pFileName, flag_t uFlags, bool bHighPriority)
	{
		return _ResStreamLoad( eType, (CResourcePtr &)pResource, pFileName, uFlags, bHighPriority );
	}
	
	template <typename T>
	bool CResourceManager::StreamLoad(e_resource::type  eType, CRefCounter<T,CReferenced> &pResource, IFilePtr pFile, flag_t uFlags, bool bHighPriority)
	{
		return _ResStreamLoad( eType, (CResourcePtr &)pResource, pFile, uFlags, bHighPriority );
	}
	
	template <typename T>
	bool CResourceManager::StreamLoad(e_res_group::type eType, CRefCounter<T,CReferenced> &pResource, IFilePtr pFile, flag_t uFlags, bool bHighPriority)
	{
		return _ResStreamLoad( eType, (CResourcePtr &)pResource, pFile, uFlags, bHighPriority );
	}*/

//-------------------------------------------------------------------

}	// CoreSystems
}	// Engine

#endif	// _UX_RESOURCE_MANAGER_H